package com.itheima.bean;

import com.itheima.domain.Student;
import com.itheima.mapper.StudentMapper;
import com.itheima.util.MybatisUtil;
import org.apache.ibatis.session.SqlSession;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

/**
 * 用于生产service对象的工厂
 */
public class BeanFactory {

    //定义一个容器，用于存放创建出来的对象
    private static Map<String,Object> beans = null;//map的key就是配置文件的key，map的value就是配置文件value反射创建出来的对象

    //定义读取配置文件的对象
    private static ResourceBundle bundle = null;

    //静态代码块赋值
    static {//他的执行时机，是当前类的字节码进入虚拟机内存时，就执行
        try{
            //实例化map
            beans = new HashMap<>();
            //读取配置文件
            bundle = ResourceBundle.getBundle("bean");
            //获取配置文件中所有的key
            Enumeration<String> keys = bundle.getKeys();
            //遍历key的枚举
            while (keys.hasMoreElements()){
                //取出每个key
                String key = keys.nextElement();
                //根据key取出全限定类名
                String classFullPath = bundle.getString(key);
                //根据得到的全限定类名实例化对象
                Object value = Class.forName(classFullPath).newInstance();

                //生成service对象的代理对象,在代理对象中添加事务的支持
                Object proxyObject = Proxy.newProxyInstance(value.getClass().getClassLoader(),
                        value.getClass().getInterfaces(),
                        new InvocationHandler() {
                            /**
                             * 对执行的方法添加事务
                             * @param proxy
                             * @param method
                             * @param args
                             * @return
                             * @throws Throwable
                             */
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                SqlSession sqlSession = null;
                                Object rtValue = null;
                                try{
                                    sqlSession = MybatisUtil.openSession();

                                    //1.获取当前类的字节码
                                    Class serviceClass = value.getClass();
                                    //2.获取当前的类成员变量
                                    Field[] fields = serviceClass.getDeclaredFields();
                                    //3.通过反射，给类成员变量赋值
                                    for(Field field : fields){
                                        //判断是不是mapper
                                        Class mapperClass = field.getType();//得到是声明的类型字节码
                                        System.out.println(mapperClass.getPackage().toString());
                                        if(mapperClass.getPackage().toString().contains("mapper")){//当前的field得是持久层接口，是别的不行
                                            //创建dao接口的代理实现
                                            Object mapper = sqlSession.getMapper(mapperClass);
                                            //解除field的私有限制
                                            field.setAccessible(true);
                                            //把代理实现类，给当前的field赋值
                                            field.set(value,mapper);
                                        }
                                    }
                                    //4.执行方法
                                    //方法的执行：通过调用service的方法，让他是调用持久层的方法实现操作
                                    //method指的是当前service中方法
                                    rtValue = method.invoke(value,args);

                                    MybatisUtil.commit(sqlSession);
                                }catch (Exception e){
                                    e.printStackTrace();
                                    MybatisUtil.rollback(sqlSession);
                                }finally {
                                    MybatisUtil.close(sqlSession);
                                }
                                return rtValue;
                            }
                        });

                //把key和value存入map中
                beans.put(key,proxyObject);
            }
        }catch (Exception e){
            throw new ExceptionInInitializerError("初始化容器失败，程序不能继续执行");
        }
    }

    /**
     * 根据bean的名称，获取对象
     * 他就是根据key，从map中获取value
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }
}
